using System.Text;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;

namespace SimpleAPI.Controllers
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }

    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddCors(options =>
            {
                options.AddPolicy("AllowAllOrigins",
                    builder =>
                    {
                        builder.AllowAnyOrigin()
                               .AllowAnyMethod()
                               .AllowAnyHeader();
                    });
            });
            services.AddControllers();
            services.AddHttpClient();
            services.AddScoped<TokenExtractorService>();

        }

        public void Configure(IApplicationBuilder app)
        {
            app.UseRouting();

            app.UseCors("AllowAllOrigins");

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }

    public class TokenExtractorService
    {
        public string? ExtractAccessToken(HttpRequest request)
        {
            string authorizationHeader = request.Headers["Authorization"];
            if (string.IsNullOrEmpty(authorizationHeader))
            {
                return null;
            }

            return authorizationHeader;
        }
    }


    public struct Account
    {
        public string accountId;
        public string сurrency;
        public Account(string id, string currency)
        {
            accountId = id;
            сurrency = currency;
        }
    }

    [EnableCors("AllowAllOrigins")]
    [ApiController]
    [Route("/auth")]
    public class AccountController : ControllerBase
    {
        private readonly IHttpClientFactory _clientFactory;
        private readonly TokenExtractorService _tokenExtractorService;

        public AccountController(IHttpClientFactory clientFactory, TokenExtractorService tokenExtractorService)
        {
            _clientFactory = clientFactory;
            _tokenExtractorService = tokenExtractorService;
        }

        [HttpPost]
        public async Task<IActionResult> GetAccount()
        {
            try
            {
                string accessToken = _tokenExtractorService.ExtractAccessToken(Request);
                if (accessToken == null)
                {
                    return Unauthorized("Invalid or missing authorization token");
                }

                var client = _clientFactory.CreateClient();
                client.DefaultRequestHeaders.Add("Authorization", accessToken);

                var emptyJson = new StringContent("{}", Encoding.UTF8, "application/json");

                var response = await client.PostAsync("https://invest-public-api.tinkoff.ru/rest/tinkoff.public.invest.api.contract.v1.UsersService/GetAccounts", emptyJson);

                response.EnsureSuccessStatusCode();

                var responseData = await response.Content.ReadAsStringAsync();

                return Ok(responseData);
            }
            catch (Exception ex)
            {
                return StatusCode(500, $"Internal server error: {ex.Message}");
            }
        }
    }

    public struct Answer
    {
        public Counts totalAmountPortfolio;
        public Counts expectedYield;
        public List<Paper> positions;

        public Answer(Counts _totalAmountPortfolio, Counts _expectedYield, List<Paper> _positions)
        {
            totalAmountPortfolio = _totalAmountPortfolio;
            expectedYield = _expectedYield;
            positions = _positions;
        }
    }

    public struct Counts
    {
        public string units;

        public Counts(string _units)
        {
            units = _units;
        }
    }

    public struct Paper
    {
        public string figi;
        public string instrumentType;
        public Counts currentPrice;
        public Counts quantityLots;
        public string Name { get; set; }

        public Paper(string _figi, string _instrumentType, Counts _currentPrice, Counts _quantityLots)
        {
            figi = _figi;
            instrumentType = _instrumentType;
            currentPrice = _currentPrice;
            quantityLots = _quantityLots;
        }


    }

    [EnableCors("AllowAllOrigins")]
    [ApiController]
    [Route("/portfolio")]
    public class PortfolioController : ControllerBase
    {
        private readonly IHttpClientFactory _clientFactory;
        private readonly TokenExtractorService _tokenExtractorService;

        public PortfolioController(IHttpClientFactory clientFactory, TokenExtractorService tokenExtractorService)
        {
            _clientFactory = clientFactory;
            _tokenExtractorService = tokenExtractorService;
        }

        public struct InstrumentInfo
        {
            public OnePaper instrument;
            public InstrumentInfo(OnePaper _instrument)
            {
                instrument = _instrument;
            }
        }

        public struct OnePaper
        {
            public string name;

            public OnePaper(string _name)
            {
                name = _name;
            }
        }

        public struct PaperInfo
        {
            public string id_type;
            public string id;

            public PaperInfo(string _id_type, string _id)
            {
                id_type = _id_type;
                id = _id;
            }
        }

        [HttpGet("{id}")]
        public async Task<IActionResult> GetPortfolio(string id)
        {
            try
            {

                string accessToken = _tokenExtractorService.ExtractAccessToken(Request);
                if (accessToken == null)
                {
                    return Unauthorized("Invalid or missing authorization token");
                }

                var client = _clientFactory.CreateClient();

                client.DefaultRequestHeaders.Add("Authorization", accessToken);

                var content = new Account(id, "RUB");
                var jsonContent = JsonConvert.SerializeObject(content);

                var requestContent = new StringContent(jsonContent, Encoding.UTF8, "application/json");
                var response = await client.PostAsync("https://invest-public-api.tinkoff.ru/rest/tinkoff.public.invest.api.contract.v1.OperationsService/GetPortfolio", requestContent);

                response.EnsureSuccessStatusCode();

                var responseData = await response.Content.ReadAsStringAsync();
                Answer answer = JsonConvert.DeserializeObject<Answer>(responseData);
                var positionsCopy = new List<Paper>(answer.positions);

                foreach (Paper obj in positionsCopy)
                {
                    var paper = new PaperInfo("INSTRUMENT_ID_TYPE_FIGI", obj.figi);
                    var jsonPaper = JsonConvert.SerializeObject(paper);
                    var requestPaper = new StringContent(jsonPaper, Encoding.UTF8, "application/json");
                    var word = char.ToUpper(obj.instrumentType[0]) + obj.instrumentType[1..];
                    var responsePaper = await client.PostAsync($"https://invest-public-api.tinkoff.ru/rest/tinkoff.public.invest.api.contract.v1.InstrumentsService/{word}By", requestPaper);
                    responsePaper.EnsureSuccessStatusCode();

                    var responsePaperData = await responsePaper.Content.ReadAsStringAsync();

                    InstrumentInfo instrument = JsonConvert.DeserializeObject<InstrumentInfo>(responsePaperData);

                    var updatedPaper = new Paper(obj.figi, obj.instrumentType, obj.currentPrice, obj.quantityLots)
                    {
                        Name = instrument.instrument.name
                    };
                    var index = answer.positions.IndexOf(obj);
                    answer.positions[index] = updatedPaper;
                }
                var jsonAnswer = JsonConvert.SerializeObject(answer);

                return Ok(jsonAnswer);
            }
            catch (Exception ex)
            {
                return StatusCode(500, $"Internal server error: {ex.Message}");
            }
        }
    }

    [EnableCors("AllowAllOrigins")]
    [ApiController]
    [Route("/strategy")]
    public class StrategyController : ControllerBase
    {
        private readonly IHttpClientFactory _clientFactory;
        private readonly TokenExtractorService _tokenExtractorService;

        public StrategyController(IHttpClientFactory clientFactory, TokenExtractorService tokenExtractorService)
        {
            _clientFactory = clientFactory;
            _tokenExtractorService = tokenExtractorService;
        }

        public class Diversification
        {
            public double Solve(Answer answer)
            {

                List<double> sharePosition = [];
                int total = int.Parse(answer.totalAmountPortfolio.units);

                foreach (var asset in answer.positions)
                {
                    double summ = double.Parse(asset.currentPrice.units) * double.Parse(asset.quantityLots.units);
                    double percentage = summ / total;
                    sharePosition.Add(percentage);
                }

                double entYager = 0;

                foreach (double per in sharePosition)
                {
                    entYager += Math.Abs(per - (1 / sharePosition.Count));
                }

                return -entYager;

            }
        }


        public struct Result
        {
            public double result;
            public int countPapers;

            public Result(double _result, int _countPapers)
            {
                countPapers = _countPapers;
                result = _result;
            }
        }


        [HttpGet("{id}")]
        public async Task<IActionResult> GetPortfolio(string id)
        {
            try
            {

                string accessToken = _tokenExtractorService.ExtractAccessToken(Request);
                if (accessToken == null)
                {
                    return Unauthorized("Invalid or missing authorization token");
                }

                var client = _clientFactory.CreateClient();

                client.DefaultRequestHeaders.Add("Authorization", accessToken);

                var content = new Account(id, "RUB");
                var jsonContent = JsonConvert.SerializeObject(content);

                var requestContent = new StringContent(jsonContent, Encoding.UTF8, "application/json");
                var response = await client.PostAsync("https://invest-public-api.tinkoff.ru/rest/tinkoff.public.invest.api.contract.v1.OperationsService/GetPortfolio", requestContent);

                response.EnsureSuccessStatusCode();

                var responseData = await response.Content.ReadAsStringAsync();
                Answer answer = JsonConvert.DeserializeObject<Answer>(responseData);
                Diversification diversification = new Diversification();

                double result = diversification.Solve(answer);

                var jsonResult = JsonConvert.SerializeObject(new Result(result, answer.positions.Count));

                return Ok(jsonResult);
            }
            catch (Exception ex)
            {
                return StatusCode(500, $"Internal server error: {ex.Message}");
            }
        }
    }

    [EnableCors("AllowAllOrigins")]
    [ApiController]
    [Route("/surprise")]
    public class SurpriseController : ControllerBase
    {
        private readonly IHttpClientFactory _clientFactory;
        private readonly TokenExtractorService _tokenExtractorService;

        public SurpriseController(IHttpClientFactory clientFactory, TokenExtractorService tokenExtractorService)
        {
            _clientFactory = clientFactory;
            _tokenExtractorService = tokenExtractorService;
        }

        public struct Instruments
        {
            public List<OneInstrument> instruments;
            public Instruments(List<OneInstrument> _instruments)
            {
                instruments = _instruments;
            }
        }

        public struct OneInstrument
        {
            public string figi;
            public string name;
            public string sector;
            public string ticker;


            public OneInstrument(string _figi, string _name, string _sector, string _ticker)
            {
                figi = _figi;
                name = _name;
                sector = _sector;
                ticker = _ticker;
            }
        }

        [HttpGet]
        public async Task<IActionResult> GetPaper()
        {
            try
            {

                string accessToken = _tokenExtractorService.ExtractAccessToken(Request);
                if (accessToken == null)
                {
                    return Unauthorized("Invalid or missing authorization token");
                }

                var client = _clientFactory.CreateClient();

                client.DefaultRequestHeaders.Add("Authorization", accessToken);

                var requestContent = new StringContent("{}", Encoding.UTF8, "application/json");
                var response = await client.PostAsync("https://invest-public-api.tinkoff.ru/rest/tinkoff.public.invest.api.contract.v1.InstrumentsService/Shares", requestContent);

                response.EnsureSuccessStatusCode();

                var responseData = await response.Content.ReadAsStringAsync();
                Instruments instruments = JsonConvert.DeserializeObject<Instruments>(responseData);
                Random rnd = new Random();

                int value = rnd.Next(instruments.instruments.Count);
                OneInstrument randomInstrument = instruments.instruments[value];

                var jsonRandomInstrument = JsonConvert.SerializeObject(randomInstrument);
                return Ok(jsonRandomInstrument);

            }
            catch (Exception ex)
            {
                return StatusCode(500, $"Internal server error: {ex.Message}");
            }
        }
    }

    [EnableCors("AllowAllOrigins")]
    [ApiController]
    [Route("/advice")]
    public class AdviceController : ControllerBase
    {
        private readonly IHttpClientFactory _clientFactory;
        private readonly TokenExtractorService _tokenExtractorService;

        public AdviceController(IHttpClientFactory clientFactory, TokenExtractorService tokenExtractorService)
        {
            _clientFactory = clientFactory;
            _tokenExtractorService = tokenExtractorService;
        }

        [HttpGet("{id}")]
        public async Task<IActionResult> GetPortfolio(string id)
        {
            try
            {

                string accessToken = _tokenExtractorService.ExtractAccessToken(Request);
                if (accessToken == null)
                {
                    return Unauthorized("Invalid or missing authorization token");
                }

                var client = _clientFactory.CreateClient();

                client.DefaultRequestHeaders.Add("Authorization", accessToken);

                var content = new Account(id, "RUB");
                var jsonContent = JsonConvert.SerializeObject(content);

                var requestContent = new StringContent(jsonContent, Encoding.UTF8, "application/json");
                var response = await client.PostAsync("https://invest-public-api.tinkoff.ru/rest/tinkoff.public.invest.api.contract.v1.OperationsService/GetPortfolio", requestContent);

                response.EnsureSuccessStatusCode();

                var responseData = await response.Content.ReadAsStringAsync();
                Answer answer = JsonConvert.DeserializeObject<Answer>(responseData);
                Dictionary<string, int> instrumentCounts = [];

                var translateData = new Dictionary<string, string>()
                {
                    { "etf", "Фонд"},
                    { "bond", "Облигация"},
                    { "share", "Акция"},
                    {"currency", "Валюта"}
                };

                foreach (var position in answer.positions)
                {
                    string instrumentType = position.instrumentType;
                    if (!instrumentCounts.ContainsKey(instrumentType))
                    {
                        instrumentCounts[instrumentType] = 0;
                    }
                }

                foreach (var position in answer.positions)
                {
                    string instrumentType = position.instrumentType;
                    instrumentCounts[instrumentType]++;
                }

                var dataArray = new List<object>();
                foreach (var kvp in instrumentCounts)
                {
                    dataArray.Add(new { name = translateData[kvp.Key], value = kvp.Value });
                }

                var json = JsonConvert.SerializeObject(dataArray);

                return Ok(json);
            }
            catch (Exception ex)
            {
                return StatusCode(500, $"Internal server error: {ex.Message}");
            }
        }
    }

}
